#pragma once
#include "util.h"
#include <unordered_map>
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>

typedef enum {UNLOGIN, LOGIN} ss_statu;
class session {
    private:
        uint64_t _ssid;//标识符
        uint64_t _uid;//session对应的用户ID
        ss_statu _statu;//用户状态：未登录，已登录
        wsserver_t::timer_ptr _tp;//session关联的定时器
    public:
        session(uint64_t ssid): _ssid(ssid){ DBG_Log("SESSION %p 被创建！！", this); }
        ~session() { DBG_Log("SESSION %p 被释放！！", this); }
        uint64_t ssid() { return _ssid; }
        void set_statu(ss_statu statu) { _statu = statu; }
        void set_user(uint64_t uid) { _uid = uid; }
        uint64_t get_user() { return _uid; }
        bool is_login() { return (_statu == LOGIN); }
        void set_timer(const wsserver_t::timer_ptr &tp) { _tp = tp;}
        wsserver_t::timer_ptr& get_timer() { return _tp; }
};


#define SESSION_TIMEOUT 30000
#define SESSION_FOREVER -1
using session_ptr = std::shared_ptr<session>;
class session_manager {
    private:
        uint64_t _next_ssid;
        std::mutex _mutex;
        std::unordered_map<uint64_t, session_ptr> _session;
        wsserver_t *_server;
    public:
        session_manager(wsserver_t *srv): _next_ssid(1), _server(srv){
            DBG_Log("session管理器初始化完毕！");
        }
        ~session_manager() { DBG_Log("session管理器即将销毁！"); }
        session_ptr create_session(uint64_t uid, ss_statu statu) {
            std::unique_lock<std::mutex> lock(_mutex);
            session_ptr ssp(new session(_next_ssid));
            ssp->set_statu(statu);
            ssp->set_user(uid);
            _session.insert(std::make_pair(_next_ssid,ssp));
            _next_ssid++;
            return ssp;
          
        }
        void append_session(const session_ptr &ssp) {
             std::unique_lock<std::mutex> lock(_mutex);
             _session.insert(std::make_pair(ssp->ssid(),ssp));
             return;
           
        }
        session_ptr get_session_by_ssid(uint64_t ssid) {
             std::unique_lock<std::mutex> lock(_mutex);
            auto it=_session.find(ssid);
            if(it==_session.end())
            {
                DBG_Log("%ld ssid对应的sesson不存在",ssid);
                return session_ptr();
            }    
            return it->second;       
        }
        void remove_session(uint64_t ssid) {
             std::unique_lock<std::mutex> lock(_mutex);
             _session.erase(ssid);

             
        }
        void set_session_expire_time(uint64_t ssid, int ms) {
            //依赖于websocketpp的定时器来完成session生命周期的管理。
            // 登录之后，创建session，session需要在指定时间无通信后删除
            // 但是进入游戏大厅，或者游戏房间，这个session就应该永久存在
            // 等到退出游戏大厅，或者游戏房间，这个session应该被重新设置为临时，在长时间无通信后被删除
            session_ptr sp=get_session_by_ssid(ssid);
            if(sp.get()==nullptr)
            {
                DBG_Log("%ld ssid对应的sesson不存在",ssid);
                return ;
            }
           wsserver_t::timer_ptr tp=sp->get_timer();
                // 1. 在session永久存在的情况下，设置永久存在
            if(tp.get()==nullptr&&ms==SESSION_FOREVER)
            {
                return;
            }
               
                // 2. 在session永久存在的情况下，设置指定时间之后被删除的定时任务
            if(tp.get()==nullptr&&ms==SESSION_TIMEOUT)
            {
                wsserver_t::timer_ptr temp=_server->set_timer(SESSION_TIMEOUT,std::bind(\
                    &session_manager::remove_session,this,ssid));
                sp->set_timer(temp);
                return ;


            }
              
           
                // 3. 在session设置了定时删除的情况下，将session设置为永久存在
                // 删除定时任务--- stready_timer删除定时任务会导致任务直接被执行
               //因为这个取消定时任务并不是立即取消的
                //因此重新给session管理器中，添加一个session信息, 且添加的时候需要使用定时器，而不是立即添加
               //将session关联的定时器设置为空
               if(tp.get()!=nullptr&&ms==SESSION_FOREVER)
               {
                    tp->cancel();
                    sp->set_timer(wsserver_t::timer_ptr());
                    _server->set_timer(0,std::bind(&session_manager::append_session,this,sp));
               }
             
                // 4. 在session设置了定时删除的情况下，将session重置删除时间。
              if(tp.get()!=nullptr&&ms==SESSION_TIMEOUT)
               {
                    tp->cancel();
                    _server->set_timer(0,std::bind(&session_manager::append_session,this,sp));
                    wsserver_t::timer_ptr temp=_server->set_timer(SESSION_TIMEOUT,std::bind(\
                    &session_manager::remove_session,this,ssid));
                    sp->set_timer(temp);
                    return ;

               }
                
            }
        };
